
#include <BGroup>
#include "member_BGroup.h"
#include "member_BShape.h"
#include "member_BNode.h"
#include "member_BRestraint.h"
#include "member_BObject.h"

using namespace BWE;

#define member					(*(member_BGroup*)_ptr)
#define member_allocate()		_ptr = new member_BGroup(this)
#define member_release()		delete (member_BGroup*)_ptr

BGroup::BGroup(BGroup* parent)
{
	member_allocate();
	if (parent)
		parent->insert(this);
}
BGroup::BGroup(const BString& name, BGroup* parent) : BObject(name)
{
	member_allocate();
	if (parent)
		parent->insert(this);
}
BGroup::BGroup(const BGroup& other) : BObject(other)
{
	member_allocate();
	this->copy(&other);
}
BGroup::~BGroup()
{
	while (BShape* shape = member.shapes.last())
		remove(shape);
	while (BNode* node = member.nodes.last())
		remove(node);
	while (BRestraint* restraint = member.restraints.last())
		remove(restraint);
	for (int i = 0; i < member.children.size(); i++)
	{
		BGroup* child = member.children[i];
		group_member(child)->parent = 0;
	}
	member_release();
}

void BGroup::copy(const BGroup* other)
{
	object_member(this)->name = object_member(other)->name;

	member.visible = group_member(other)->visible;
	member.box = group_member(other)->box;
	member.dirtyBox = group_member(other)->dirtyBox;
	member.space = group_member(other)->space;
	member.dirtySpace = group_member(other)->dirtySpace;

	for (int i = 0; i < group_member(other)->shapes.size(); i++)
	{
		BShape* shape = group_member(other)->shapes[i];
		this->insert(shape->clone());
	}

	for (int i = 0; i < group_member(other)->nodes.size(); i++)
	{
		BNode* node = group_member(other)->nodes[i];
		this->insert(node->clone());
	}

	for (int i = 0; i < group_member(other)->children.size(); i++)
	{
		BGroup* group = group_member(other)->children[i];
		this->insert(group->clone());
	}

	for (int i = 0; i < group_member(other)->restraints.size(); i++)
	{
		BRestraint* restraint = group_member(other)->restraints[i];
		this->insert(restraint->clone());
	}
}
BGroup* BGroup::clone() const
{
	BGroup* group = new BGroup();
	group->copy(this);
	return group;
}

bool BGroup::setVisible(bool visible)
{
	if (member.visible != visible)
	{
		member.visible = visible;
		if (member.visible)
			this->emit(Signal_Show, true);
		else
			this->emit(Signal_Hide, false);
		member.raiseSignal(Signal_Freshed);
		return true;
	}
	return false;
}
bool BGroup::visible() const
{
	return member.visible;
}

bool BGroup::empty() const
{
	return member.shapes.empty() && member.nodes.empty() && member.restraints.empty() && member.children.empty();
}
const BBox& BGroup::box() const
{
	if (member.dirtyBox)
	{
		BSpace space;
		if (BShape* shape = member.shapes.first())
			space.reset(shape->box().center());
		else if (BNode* node = member.nodes.first())
			space.reset(node->box().center());
		else if (BGroup* child = member.children.first())
			space.reset(child->box().center());
		for (int i = 0; i < member.shapes.size(); i++)
		{
			BShape* shape = member.shapes[i];
			const BBox& box = shape->box();
			space.expand(box.v0());
			space.expand(box.v1());
			space.expand(box.v2());
			space.expand(box.v3());
			space.expand(box.v4());
			space.expand(box.v5());
			space.expand(box.v6());
			space.expand(box.v7());
		}
		for (int i = 0; i < member.nodes.size(); i++)
		{
			BNode* node = member.nodes[i];
			const BBox& box = node->box();
			space.expand(box.v0());
			space.expand(box.v1());
			space.expand(box.v2());
			space.expand(box.v3());
			space.expand(box.v4());
			space.expand(box.v5());
			space.expand(box.v6());
			space.expand(box.v7());
		}
		for (int i = 0; i < member.children.size(); i++)
		{
			BGroup* group = member.children[i];
			const BBox& box = group->box();
			space.expand(box.v0());
			space.expand(box.v1());
			space.expand(box.v2());
			space.expand(box.v3());
			space.expand(box.v4());
			space.expand(box.v5());
			space.expand(box.v6());
			space.expand(box.v7());
		}
		member.box = space;
		member.dirtyBox = false;
	}
	return member.box;
}
const BSpace& BGroup::space() const
{
	if (member.dirtySpace)
	{
		member.space.reset();
		if (BShape* shape = member.shapes.first())
			member.space = shape->space();
		else if (BNode* node = member.nodes.first())
			member.space = node->space();
		else if (BGroup* child = member.children.first())
			member.space = child->space();
		for (int i = 0; i < member.shapes.size(); i++)
		{
			member.space.expand(member.shapes[i]->space());
		}
		for (int i = 0; i < member.nodes.size(); i++)
		{
			member.space.expand(member.nodes[i]->space());
		}
		for (int i = 0; i < member.children.size(); i++)
		{
			member.space.expand(member.children[i]->space());
		}
		member.dirtySpace = false;
	}
	return member.space;
}

bool BGroup::insert(BShape* shape)
{
	if (!shape)
		return false;
	if (member.shapes.contain(shape))
		return true;
	member.shapes.insert(shape);
	member.connect(shape, Signal_Freshed, &member_BGroup::slotShapeFreshed);
	member.connect(shape, Signal_Dirty, &member_BGroup::slotShapeDirty);
	member.connect(shape, Signal_Flushed, &member_BGroup::slotShapeFlushed);
	member.connect(shape, Signal_Transformed, &member_BGroup::slotShapeTransformed);
	if (!member.space.contain(shape->space()))
		member.raiseDirty();
	member.raiseSignal(Signal_Insert, BValue(shape, this));
	return true;
}
bool BGroup::remove(BShape* shape)
{
	if (!shape)
		return false;
	if (!member.shapes.contain(shape))
		return false;
	BHolder<BShape> holder(shape);
	member.disconnect(shape, Signal_Transformed);
	member.disconnect(shape, Signal_Flushed);
	member.disconnect(shape, Signal_Dirty);
	member.disconnect(shape, Signal_Freshed);
	member.shapes.remove(shape);
	if (member.space.edged(shape->space()))
		member.raiseDirty();
	member.raiseSignal(Signal_Remove, BValue(shape, this));
	return true;
}
bool BGroup::contain(BShape* shape)
{
	return member.shapes.contain(shape);
}

int BGroup::shapeCount() const
{
	return member.shapes.size();
}
BShape* BGroup::shape(int index) const
{
	return member.shapes(index);
}
BShape* BGroup::shape(const BString& name) const
{
	for (int i = 0; i < member.shapes.size(); i++)
	{
		BShape* shape = member.shapes[i];
		if (shape->name() == name)
			return shape;
	}
	return 0;
}

bool BGroup::insert(BNode* node)
{
	if (!node)
		return false;
	if (node_member(node)->group == this)
		return true;
	member.nodes.insert(node);
	if (node_member(node)->group)
		node_member(node)->group->remove(node);
	node_member(node)->group = this;
	member.connect(node, Signal_Freshed, &member_BGroup::slotNodeFreshed);
	member.connect(node, Signal_Dirty, &member_BGroup::slotNodeDirty);
	member.connect(node, Signal_Flushed, &member_BGroup::slotNodeFlushed);
	member.connect(node, Signal_Transformed, &member_BGroup::slotNodeTransformed);
	if (!member.space.contain(node->space()))
		member.raiseDirty();
	member.raiseSignal(Signal_Insert, BValue(node, this));
	return true;
}
bool BGroup::remove(BNode* node)
{
	if (!node)
		return false;
	if (node_member(node)->group != this)
		return false;
	BNodeHolder holder(node);
	member.disconnect(node, Signal_Freshed);
	member.disconnect(node, Signal_Dirty);
	member.disconnect(node, Signal_Flushed);
	member.disconnect(node, Signal_Transformed);
	member.nodes.remove(node);
	node_member(node)->group = 0;
	if (member.space.edged(node->space()))
		member.raiseDirty();
	member.raiseSignal(Signal_Remove, BValue(node, this));
	return true;
}
bool BGroup::contain(BNode* node)
{
	return member.nodes.contain(node);
}

int BGroup::nodeCount() const
{
	return member.nodes.size();
}
BNode* BGroup::node(int index) const
{
	return member.nodes(index);
}
BNode* BGroup::node(const BString& name) const
{
	for (int i = 0; i < member.nodes.size(); i++)
	{
		BNode* node = member.nodes[i];
		if (node->name() == name)
			return node;
	}
	return 0;
}

BBody* BGroup::body(int index) const
{
	BNode* node = member.nodes(index);
	return dynamic_cast<BBody*>(node);
}
BBody* BGroup::body(const BString& name) const
{
	for (int i = 0; i < member.nodes.size(); i++)
	{
		BNode* node = member.nodes[i];
		if (node->name() == name)
			return dynamic_cast<BBody*>(node);
	}
	return 0;
}

bool BGroup::insert(BRestraint* restraint)
{
	if (!restraint)
		return false;
	if (restraint_member(restraint)->group == this)
		return true;
	member.restraints.insert(restraint);
	if (restraint_member(restraint)->group)
		restraint_member(restraint)->group->remove(restraint);
	restraint_member(restraint)->group = this;
	member.connect(restraint, Signal_Changed, &member_BGroup::slotRestraintChanged);
	member.raiseSignal(Signal_Insert, BValue(restraint, this));
	return true;
}
bool BGroup::remove(BRestraint* restraint)
{
	if (!restraint)
		return false;
	if (restraint_member(restraint)->group != this)
		return false;
	BHolder<BRestraint> holder(restraint);
	member.disconnect(restraint, Signal_Changed);
	member.restraints.remove(restraint);
	restraint_member(restraint)->group = 0;
	member.raiseSignal(Signal_Remove, BValue(restraint, this));
	return true;
}

bool BGroup::contain(BRestraint* restraint)
{
	return member.restraints.contain(restraint);
}

int BGroup::restraintCount() const
{
	return member.restraints.size();
}
BRestraint* BGroup::restraint(int index) const
{
	return member.restraints(index);
}
BRestraint* BGroup::restraint(const BString& name) const
{
	for (int i = 0; i < member.restraints.size(); i++)
	{
		BRestraint* constratin = member.restraints[i];
		if (constratin->name() == name)
			return constratin;
	}
	return 0;
}

bool BGroup::contain(const BString& name) const
{
	for (int i = 0; i < member.shapes.size(); i++)
		if (member.shapes[i]->name() == name)
			return true;
	for (int i = 0; i < member.nodes.size(); i++)
		if (member.nodes[i]->name() == name)
			return true;
	for (int i = 0; i < member.children.size(); i++)
		if (member.children[i]->name() == name)
			return true;
	for (int i = 0; i < member.restraints.size(); i++)
		if (member.restraints[i]->name() == name)
			return true;
	return false;
}

BObject* BGroup::find(const BString& name)
{
	if (BGroup* group = findGroup(name))
		return group;
	if (BShape* shape = findShape(name))
		return shape;
	if (BNode* node = findNode(name))
		return node;
	if (BRestraint* restraint = findRestraint(name))
		return restraint;
	return 0;
}
const BObject* BGroup::find(const BString& name) const
{
	if (const BGroup* group = findGroup(name))
		return group;
	if (const BShape* shape = findShape(name))
		return shape;
	if (const BNode* node = findNode(name))
		return node;
	if (const BRestraint* restraint = findRestraint(name))
		return restraint;
	return 0;
}

BGroup* BGroup::findGroup(const BString& name)
{
	for (int i = 0; i < member.children.size(); i++)
	{
		BGroup* child = member.children[i];
		if (child->name() == name)
			return child;
	}
	return 0;
}
const BGroup* BGroup::findGroup(const BString& name) const
{
	for (int i = 0; i < member.children.size(); i++)
	{
		const BGroup* child = member.children[i];
		if (child->name() == name)
			return child;
	}
	return 0;
}

BShape* BGroup::findShape(const BString& name)
{
	for (int i = 0; i < member.shapes.size(); i++)
	{
		BShape* shape = member.shapes[i];
		if (shape->name() == name)
			return shape;
	}
	return 0;
}
const BShape* BGroup::findShape(const BString& name) const
{
	for (int i = 0; i < member.shapes.size(); i++)
	{
		const BShape* shape = member.shapes[i];
		if (shape->name() == name)
			return shape;
	}
	return 0;
}

BNode* BGroup::findNode(const BString& name)
{
	for (int i = 0; i < member.nodes.size(); i++)
	{
		BNode* node = member.nodes[i];
		if (node->name() == name)
			return node;
	}
	for (int i = 0; i < member.children.size(); i++)
	{
		BNode* node = member.children[i]->findNode(name);
		if (node)
			return node;
	}
	return 0;
}
const BNode* BGroup::findNode(const BString& name) const
{
	for (int i = 0; i < member.nodes.size(); i++)
	{
		const BNode* node = member.nodes[i];
		if (node->name() == name)
			return node;
	}
	return 0;
}

BBody* BGroup::findBody(const BString& name)
{
	for (int i = 0; i < member.nodes.size(); i++)
	{
		BNode* node = member.nodes[i];
		if (node->name() == name)
			return dynamic_cast<BBody*>(node);
	}
	for (int i = 0; i < member.children.size(); i++)
	{
		BNode* node = member.children[i]->findNode(name);
		if (node)
			return dynamic_cast<BBody*>(node);
	}
	return 0;
}
const BBody* BGroup::findBody(const BString& name) const
{
	for (int i = 0; i < member.nodes.size(); i++)
	{
		const BNode* node = member.nodes[i];
		if (node->name() == name)
			return dynamic_cast<const BBody*>(node);
	}
	for (int i = 0; i < member.children.size(); i++)
	{
		BNode* node = member.children[i]->findNode(name);
		if (node)
			return dynamic_cast<const BBody*>(node);
	}
	return 0;
}

BRestraint* BGroup::findRestraint(const BString& name)
{
	for (int i = 0; i < member.restraints.size(); i++)
	{
		BRestraint* restraint = member.restraints[i];
		if (restraint->name() == name)
			return restraint;
	}
	return 0;
}
const BRestraint* BGroup::findRestraint(const BString& name) const
{
	for (int i = 0; i < member.restraints.size(); i++)
	{
		const BRestraint* restraint = member.restraints[i];
		if (restraint->name() == name)
			return restraint;
	}
	return 0;
}

BString BGroup::path(const char* split) const
{
	BString str;
	const BGroup* group = this;
	while (group)
	{
		if (str.size())
			str.prepend(split);
		str.prepend(group->name());
		group = group->parent();
	}
	return str;
}

BObject* BGroup::locate(const BString& path)
{
	if (path.empty())
		return this;
	BGroup* group = this;
	if (path.contain('/'))
	{
		BStringArray textlst = path.split('/');
		for (int i = 0; i < textlst.size() - 1; i++)
		{
			const BString& text = textlst[i];
			group = group->child(text);
			if (!group)
				return 0;
		}
	}
	const char* name = path.name();
	if (BGroup* child = group->child(name))
		return child;
	if (BShape* shape = group->shape(name))
		return shape;
	if (BNode* node = group->node(name))
		return node;
	if (BRestraint* restraint = group->restraint(name))
		return restraint;
	return 0;
}
const BObject* BGroup::locate(const BString& path) const
{
	if (path.empty())
		return this;
	const BGroup* group = this;
	if (path.contain('/'))
	{
		BStringArray textlst = path.split('/');
		for (int i = 0; i < textlst.size() - 1; i++)
		{
			const BString& text = textlst[i];
			group = group->child(text);
			if (!group)
				return 0;
		}
	}
	const char* name = path.name();
	if (BGroup* child = group->child(name))
		return child;
	if (BShape* shape = group->shape(name))
		return shape;
	if (BNode* node = group->node(name))
		return node;
	if (BRestraint* restraint = group->restraint(name))
		return restraint;
	return 0;
}

BBody* BGroup::locateBody(const BString& path)
{
	if (path.empty())
		return 0;
	BGroup* group = this;
	if (path.contain('/'))
	{
		BStringArray textlst = path.split('/');
		for (int i = 0; i < textlst.size() - 1; i++)
		{
			const BString& text = textlst[i];
			group = group->child(text);
			if (!group)
				return 0;
		}
	}
	return group->body(path.name());
}
const BBody* BGroup::locateBody(const BString& path) const
{
	if (path.empty())
		return 0;
	const BGroup* group = this;
	if (path.contain('/'))
	{
		BStringArray textlst = path.split('/');
		for (int i = 0; i < textlst.size() - 1; i++)
		{
			const BString& text = textlst[i];
			group = group->child(text);
			if (!group)
				return 0;
		}
	}
	return group->body(path.name());
}

BNode* BGroup::locateNode(const BString& path)
{
	if (path.empty())
		return 0;
	BGroup* group = this;
	if (path.contain('/'))
	{
		BStringArray textlst = path.split('/');
		for (int i = 0; i < textlst.size() - 1; i++)
		{
			const BString& text = textlst[i];
			group = group->child(text);
			if (!group)
				return 0;
		}
	}
	return group->node(path.name());
}
const BNode* BGroup::locateNode(const BString& path) const
{
	if (path.empty())
		return 0;
	const BGroup* group = this;
	if (path.contain('/'))
	{
		BStringArray textlst = path.split('/');
		for (int i = 0; i < textlst.size() - 1; i++)
		{
			const BString& text = textlst[i];
			group = group->child(text);
			if (!group)
				return 0;
		}
	}
	return group->node(path.name());
}

BShape* BGroup::locateShape(const BString& path)
{
	if (path.empty())
		return 0;
	BGroup* group = this;
	if (path.contain('/'))
	{
		BStringArray textlst = path.split('/');
		while (textlst.size() > 1)
		{
			const BString& text = textlst.first();
			BGroup* child = group->child(text);
			if (!child)
				break;
			group = child;
			textlst.remove(0);
		}
		if (textlst.size() == 2)
		{
			if (BNode* node = group->node(textlst.first()))
				return node->shape(textlst.last());
		}
		if (textlst.size() == 1)
		{
			return group->shape(textlst.last());
		}
		return 0;
	}
	return group->shape(path.name());
}
const BShape* BGroup::locateShape(const BString& path) const
{
	if (path.empty())
		return 0;
	const BGroup* group = this;
	if (path.contain('/'))
	{
		BStringArray textlst = path.split('/');
		while (textlst.size() > 1)
		{
			const BString& text = textlst.first();
			const BGroup* child = group->child(text);
			if (!child)
				break;
			group = child;
			textlst.remove(0);
		}
		if (textlst.size() == 2)
		{
			if (const BNode* node = group->node(textlst.first()))
				return node->shape(textlst.last());
		}
		if (textlst.size() == 1)
		{
			return group->shape(textlst.last());
		}
		return 0;
	}
	return group->shape(path.name());
}

bool BGroup::clear()
{
	bool result = false;
	if (member.shapes.size())
	{
		while (BShape* shape = member.shapes.last())
			remove(shape);
		result = true;
	}
	if (member.nodes.size())
	{
		while (BNode* node = member.nodes.last())
			remove(node);
		result = true;
	}
	if (member.restraints.size())
	{
		while (BRestraint* restraint = member.restraints.last())
			remove(restraint);
		result = true;
	}
	if (member.children.size())
	{
		while (BGroup* child = member.children.last())
			remove(child);
		result = true;
	}
	return result;
}
bool BGroup::clearShapes()
{
	if (member.shapes.size())
	{
		while (BShape* shape = member.shapes.last())
			remove(shape);
		return true;
	}
	return false;
}
bool BGroup::clearNodes()
{
	if (member.nodes.size())
	{
		while (BNode* node = member.nodes.last())
			remove(node);
		return true;
	}
	return false;
}
bool BGroup::clearRestraints()
{
	if (member.restraints.size())
	{
		while (BRestraint* restraint = member.restraints.last())
			remove(restraint);
		return true;
	}
	return false;
}

BGroup* BGroup::root()
{
	BGroup* group = this;
	while (group_member(group)->parent)
		group = group_member(group)->parent;
	return group;
}
const BGroup* BGroup::root() const
{
	const BGroup* group = this;
	while (group_member(group)->parent)
		group = group_member(group)->parent;
	return group;
}

bool BGroup::setParent(BGroup* parent)
{
	if (member.parent != parent && parent != this)
	{
		BHolder<BGroup> holder;
		if (member.parent)
		{
			holder = this;
			if (!member.parent->remove(this))
				return false;
		}
		if (parent)
		{
			if (!parent->insert(this))
				return false;
			holder = 0;
		}
		return true;
	}
	return false;
}
const BGroup* BGroup::parent() const
{
	return member.parent;
}
BGroup* BGroup::parent()
{
	return member.parent;
}

bool BGroup::insert(BGroup* child)
{
	if (child && child != this && !member.children.contain(child))
	{
		BHolder<BGroup> holder(child);
		if (group_member(child)->parent)
			group_member(child)->parent->remove(child);
		group_member(child)->parent = this;
		member.children.insert(child);
		if (!member.space.contain(child->space()))
			member.raiseDirty();
		object_member(child)->emit(Signal_ParentChanged, BValue(this, true));
		member.raiseSignal(Signal_Insert, BValue(child, this));
		return true;
	}
	return false;
}
bool BGroup::remove(BGroup* child)
{
	if (child && child != this && member.children.contain(child))
	{
		BHolder<BGroup> holder(child);
		group_member(child)->parent = 0;
		member.children.remove(child);
		if (!member.space.contain(child->space()))
			member.raiseDirty();
		object_member(child)->emit(Signal_ParentChanged);
		member.raiseSignal(Signal_Remove, BValue(child, this));
		return true;
	}
	return false;
}
bool BGroup::contain(BGroup* child)
{
	return member.children.contain(child);
}

int BGroup::childCount() const
{
	return member.children.size();
}
BGroup* BGroup::child(int index) const
{
	return member.children(index);
}
BGroup* BGroup::child(const BString& name) const
{
	for (int i = 0; i < member.children.size(); i++)
	{
		if (member.children[i]->name() == name)
			return member.children[i];
	}
	return 0;
}

bool BGroup::clearChildren()
{
	if (member.children.size())
	{
		while (BGroup* child = member.children.last())
			remove(child);
		return true;
	}
	return false;
}

void BGroup::update(BReal time)
{

}
